﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace Luna.Framework.Impl
{
	public class DefaultContext: IAppContext		
	{
		private string _name;
		private IAppContext _parent;
		private List<object> _services;
		private IDictionary<object, WeakReference> properties;
		private object host;

		public DefaultContext(IAppContext parentContext, string name)
		{
			this._parent = parentContext;
			_name = name;
			_services = new List<object>();
			properties = new Dictionary<object, WeakReference>();
		}

		public override string ToString() {
			return "{0: Name = {1}, Parent={2}}".format(this.GetType().Name, Name, Parent != null ? Parent.Name : "null");
		}

		protected void clearProperties() {
			properties.Clear();
		}

		#region IAppContext Members

		public IAppContext Root
		{
			get { return this.Parent != null ? this.Parent.Root : this; }
		}

		public IAppContext Parent
		{
			get { return _parent; }
		}

		public object Host {
			get { return host; }
			set {
				if (host == null) {
					host = value;
				}
			}
		}

		public string Name
		{
			get { return _name.isNotNullAndEmpty() ? _name : host != null? host.getPropertyValue<string>("Name", null) : null ; }
		}

		public IAppContext createChildContext(string contextName)
		{
			return new DefaultContext(this, contextName);
		}

		public object this[object key] {
			get { return properties.ContainsKey(key) && properties[key].IsAlive ? properties[key].Target : Parent != null ? Parent[key] : null; }
			set {
				if (value != null) {
					properties[key] = new WeakReference(value);
				}
			}
		}
		#endregion

		#region IServiceProvider Members

		public object addService(object serviceInstance)
		{
			if (_services.IndexOf(serviceInstance) < 0)
				_services.Add(serviceInstance);
			return serviceInstance;
		}

		public ServiceContract getService<ServiceContract>() where ServiceContract : class
		{
			return getService<ServiceContract>(s => true);
		}

		public ServiceContract getService<ServiceContract>(Predicate<ServiceContract> pred) where ServiceContract : class
		{
			ServiceContract result = null;
			//	= (ServiceContract) _services.Find(
			//    o => o is ServiceContract && pred((ServiceContract)o)
			//);
			Object o = (from obj in _services
						where (obj is ServiceContract && pred(obj as ServiceContract)) ||
							   (obj is IServicePlaceHolder && (obj as IServicePlaceHolder).supportContract(typeof(ServiceContract)) &&
								   pred(result = (obj as IServicePlaceHolder).createInstance(this) as ServiceContract))
						select obj).FirstOrDefault();

			if (o is ServiceContract)
				result = (o as ServiceContract);

			if (result == null && Parent != null)
				result = Parent.getService(pred);
			return result;
		}

		public IEnumerable<ServiceContract> findServices<ServiceContract>() where ServiceContract : class
		{
			var q = from ins in _services
					where (ins is ServiceContract)
					select (ServiceContract)ins;
			if (Parent != null)
				q = q.Concat(Parent.findServices<ServiceContract>());
			return q;
		}

		public IEnumerable<ServiceContract> findServices<ServiceContract>(Predicate<ServiceContract> pred) where ServiceContract : class {
			var q = from ins in _services
					where (ins is ServiceContract) && pred(ins as ServiceContract)
					select (ServiceContract)ins;
			if (Parent != null)
				q = q.Concat(Parent.findServices<ServiceContract>(pred));
			return q;
		}

		public void removeService(object serviceInstance)
		{
			_services.Remove(serviceInstance);
		}

		public void removeServices<ServiceContract>(Predicate<ServiceContract> pred)
		{
			throw new NotImplementedException();
		}

		#endregion
	}
}
